package org.apache.shiro.web.filter;

import java.io.IOException;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.cas.CasToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.apache.shiro.web.validator.LoginValidator;
import org.openkoala.security.facade.SecurityAccessFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * This filter validates the CAS service ticket to authenticate the user. It
 * must be configured on the URL recognized by the CAS server. For example, in
 * {@code shiro.ini}:
 * 
 * <pre>
 * [main]
 * casFilter = org.apache.shiro.cas.CasFilter
 * ...
 * 
 * [urls]
 * /shiro-cas = casFilter
 * ...
 * </pre>
 * 
 * (example : http://host:port/mycontextpath/shiro-cas)
 *
 * @since 1.2
 */
public class CasFilter extends AuthenticatingFilter {
	
	@Autowired
	private SecurityAccessFacade securityAccessFacade;

	private static Logger logger = LoggerFactory.getLogger(CasFilter.class);

	// the name of the parameter service ticket in url
	private static final String TICKET_PARAMETER = "ticket";

	// the url where the application is redirected if the CAS service ticket
	// validation failed (example : /mycontextpatch/cas_error.jsp)
	private String failureUrl;

	private String successUrl;
	
	private String casServer;

	private String service;
	
	private List<LoginValidator> validatorList;
	
	private  String casServiceUrlIn;
	  
	private String drgServiceUrlIn;  

	
	public String getCasServiceUrlIn() {
		return casServiceUrlIn;
	}

	public void setCasServiceUrlIn(String casServiceUrlIn) {
		this.casServiceUrlIn = casServiceUrlIn;
	}

	public String getDrgServiceUrlIn() {
		return drgServiceUrlIn;
	}

	public void setDrgServiceUrlIn(String drgServiceUrlIn) {
		this.drgServiceUrlIn = drgServiceUrlIn;
	}

	/**
	 * The token created for this authentication is a CasToken containing the
	 * CAS service ticket received on the CAS service url (on which the filter
	 * must be configured).
	 * 
	 * @param request
	 *            the incoming request
	 * @param response
	 *            the outgoing response
	 * @throws Exception
	 *             if there is an error processing the request.
	 */
	@Override
	protected AuthenticationToken createToken(ServletRequest request,
			ServletResponse response) throws Exception {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		String ticket = httpRequest.getParameter(TICKET_PARAMETER);
		
		//第三方单点登录验证
		for (LoginValidator loginValidator : validatorList) {
			if (loginValidator != null && loginValidator.hasRequiredParam(httpRequest) && loginValidator.isValidate()) {
				//验证成功
				//查询DRG系统密码并传给cas-server获取ticket
				String serviceTicket = getServiceTicket(casServer, service, loginValidator.getUsername(), getPassword(loginValidator.getUsername()));
				//System.out.println("serviceTicket: "+serviceTicket);
				ticket = serviceTicket;
			}
		}
		//LoginValidator loginValidator = new PortalLoginValidator(httpRequest, ticketValidateUrl, ticketValidateMethod);
//		LoginValidator loginValidator = new ChangGengLoginValidator(httpRequest);
//		if (loginValidator != null && loginValidator.isValidate()) {
//			//验证成功
//			//查询DRG系统密码并传给cas-server获取ticket
//			String serviceTicket = getServiceTicket(casServer, service, loginValidator.getUsername(), getPassword(loginValidator.getUsername()));
//			//System.out.println("serviceTicket: "+serviceTicket);
//			ticket = serviceTicket;
//		}
		//第三方单点登录结束

		return new CasToken(ticket);
	}

	/**
	 * Execute login by creating
	 * {@link #createToken(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
	 * token} and logging subject with this token.
	 * 
	 * @param request
	 *            the incoming request
	 * @param response
	 *            the outgoing response
	 * @throws Exception
	 *             if there is an error processing the request.
	 */
	@Override
	protected boolean onAccessDenied(ServletRequest request,
			ServletResponse response) throws Exception {
		return executeLogin(request, response);
	}

	/**
	 * Returns <code>false</code> to always force authentication (user is never
	 * considered authenticated by this filter).
	 * 
	 * @param request
	 *            the incoming request
	 * @param response
	 *            the outgoing response
	 * @param mappedValue
	 *            the filter-specific config value mapped to this filter in the
	 *            URL rules mappings.
	 * @return <code>false</code>
	 */
	@Override
	protected boolean isAccessAllowed(ServletRequest request,
			ServletResponse response, Object mappedValue) {
		return false;
	}

	/**
	 * If login has been successful, redirect user to the original protected
	 * url.
	 * 
	 * @param token
	 *            the token representing the current authentication
	 * @param subject
	 *            the current authenticated subjet
	 * @param request
	 *            the incoming request
	 * @param response
	 *            the outgoing response
	 * @throws Exception
	 *             if there is an error processing the request.
	 */
	@Override
	protected boolean onLoginSuccess(AuthenticationToken token,
			Subject subject, ServletRequest request, ServletResponse response)
			throws Exception {
		issueSuccessRedirect(request, response);
		return false;
	}

	/**
	 * If login has failed, redirect user to the CAS error page (no ticket or
	 * ticket validation failed) except if the user is already authenticated, in
	 * which case redirect to the default success url.
	 * 
	 * @param token
	 *            the token representing the current authentication
	 * @param ae
	 *            the current authentication exception
	 * @param request
	 *            the incoming request
	 * @param response
	 *            the outgoing response
	 */
	@Override
	protected boolean onLoginFailure(AuthenticationToken token,
			AuthenticationException ae, ServletRequest request,
			ServletResponse response) {
		// is user authenticated or in remember me mode ?
		Subject subject = getSubject(request, response);
		if (subject.isAuthenticated() || subject.isRemembered()) {
			try {
				issueSuccessRedirect(request, response);
			} catch (Exception e) {
				logger.error("Cannot redirect to the default success url", e);
			}
		} else {
			try {
				WebUtils.issueRedirect(request, response, failureUrl);
			} catch (IOException e) {
				logger.error("Cannot redirect to failure url : {}", failureUrl,
						e);
			}
		}
		return false;
	}

	public void setFailureUrl(String failureUrl) {
		this.failureUrl = failureUrl;
	}

	public static void main(String[] args) {
//		String username = getUsernameFromPortal(null, null);
		System.err.println("26a67d16c3b1f5e1e5abed7eda944f11".length());
	}

	/**
	 * @methodname :getTicket
	 * @description :从CAS服务器获取ST
	 * @param :
	 * @author :WangTeng
	 * @return :
	 * @date :2018年4月12日
	 */
	private String getServiceTicket(final String casServer, final String service, String username, String password) {
		String ticketGrantingTicket = getTicketGrantingTicket(casServer, username, password);
		if (ticketGrantingTicket == null){
			return null;
		}

		final HttpClient client = new HttpClient();

		final PostMethod post = new PostMethod(casServer + "/" + ticketGrantingTicket);

		post.setRequestBody(new NameValuePair[] { new NameValuePair("service",
				service) });

		try {
			client.executeMethod(post);

			final String response = post.getResponseBodyAsString();

			switch (post.getStatusCode()) {
			case 200:
				return response;
			default:
				logger.warn("Invalid response code (" + post.getStatusCode() + ") from CAS server!");
				logger.info("Response (1k): " + response.substring(0, Math.min(1024, response.length())));
				break;
			}
		} catch (final IOException e) {
			logger.warn(e.getMessage());
		} finally {
			post.releaseConnection();
		}

		return null;
	}

	/**
	 * @methodname    :getTicketGrantingTicket
	 * @description   :从CAS服务器获取TGT
	 * @param         :
	 * @author        :WangTeng
	 * @return        :
	 * @date          :2018年4月12日
	 */
	private String getTicketGrantingTicket(final String server,
			final String username, final String password) {
		final HttpClient client = new HttpClient();

		final PostMethod post = new PostMethod(server);

		post.setRequestBody(new NameValuePair[] {
				new NameValuePair("username", username),
				new NameValuePair("password", password) });

		try {
			client.executeMethod(post);

			final String response = post.getResponseBodyAsString();

			switch (post.getStatusCode()) {
			case 201: {
				final Matcher matcher = Pattern.compile(".*action=\".*/(.*?)\".*").matcher(response);

				if (matcher.matches()){
					//System.out.println("matcher.group(1): "+matcher.group(1));
					return matcher.group(1);
				}
				logger.warn("Successful ticket granting request, but no ticket found!");
				logger.info("Response (1k): " + response.substring(0, Math.min(1024, response.length())));
				break;
			}

			default:
				logger.warn("Invalid response code (" + post.getStatusCode() + ") from CAS server!");
				logger.info("Response (1k): " + response.substring(0, Math.min(1024, response.length())));
				break;
			}
		} catch (final IOException e) {
			e.printStackTrace();
		} finally {
			post.releaseConnection();
		}

		return null;
	}
	
	private String getPassword(String username){
		
		return securityAccessFacade.getPassword(username);
	}
	


	/**
	 * @return the successUrl
	 */
	public String getSuccessUrl() {
		return successUrl;
	}

	/**
	 * @param successUrl the successUrl to set
	 */
	public void setSuccessUrl(String successUrl) {
		this.successUrl = successUrl;
	}

	/**
	 * @return the casServer
	 */
	public String getCasServer() {
		return casServer;
	}

	/**
	 * @param casServer the casServer to set
	 */
	public void setCasServer(String casServer) {
		this.casServer = casServer;
	}
	

	/**
	 * @return the service
	 */
	public String getService() {
		return service;
	}

	/**
	 * @param service the service to set
	 */
	public void setService(String service) {
		this.service = service;
	}

	/**
	 * @return the validatorList
	 */
	public List<LoginValidator> getValidatorList() {
		return validatorList;
	}

	/**
	 * @param validatorList the validatorList to set
	 */
	public void setValidatorList(List<LoginValidator> validatorList) {
		this.validatorList = validatorList;
	}

}
